﻿using System.Diagnostics;

namespace KinonekoSoftware.Studio.Utils
{
    public delegate void ParentBuilder<TValue>(TValue parent, TValue child);

    public class FlattenDictionary<TKey, TValue>
        where TKey : class
        where TValue : class
    {
        protected readonly Dictionary<TKey, TValue>     InternalMap;
        protected readonly ObservableCollection<TValue> InternalCollection;
        private readonly   Func<TValue, TKey>           _objectIDSelector;
        private readonly   Func<TValue, TKey>           _objectParentIDSelector;
        private readonly   Predicate<TValue>            _objectHasParentSelector;
        private readonly   ParentBuilder<TValue>        _objectParentBuilder;
        private readonly   Predicate<TValue>            _isRootObjectSelector;

        public FlattenDictionary(
            Func<TValue, TKey> idSelector,
            Func<TValue, TKey> parentIDSelector,
            Predicate<TValue> hasParentSelector,
            Predicate<TValue> isRootSelector,
            ParentBuilder<TValue> builder)
        {
            _objectIDSelector        = idSelector ?? throw new ArgumentNullException(nameof(idSelector));
            _objectParentBuilder     = builder ?? throw new ArgumentNullException(nameof(builder));
            _objectParentIDSelector  = parentIDSelector ?? throw new ArgumentNullException(nameof(parentIDSelector));
            _objectHasParentSelector = hasParentSelector ?? throw new ArgumentNullException(nameof(hasParentSelector));
            _isRootObjectSelector    = isRootSelector ?? throw new ArgumentNullException(nameof(isRootSelector));
            InternalMap              = new Dictionary<TKey, TValue>();
            InternalCollection       = new ObservableCollection<TValue>();

            Map                = new ReadOnlyDictionary<TKey, TValue>(InternalMap);
            ReadonlyCollection = new ReadOnlyObservableCollection<TValue>(InternalCollection);
        }
        

        public void Rebuild(ICollection<TValue> collection)
        {
            if (collection is null)
            {
                return;
            }

            //
            // 清空内容
            InternalMap.Clear();
            InternalCollection.Clear();

            foreach (var value in collection)
            {
                var key = _objectIDSelector(value);
                InternalMap.TryAdd(key, value);
            }

            foreach (var value in collection)
            {
                var parentID = _objectParentIDSelector(value);

                if (_objectHasParentSelector(value))
                {
                    if (InternalMap.TryGetValue(parentID, out var parent))
                    {

                        _objectParentBuilder(parent, value);
                    }
                    else
                    {
                        var key = _objectIDSelector(value);
                        Trace.WriteLine($"missing association id:{key}, parent id:{parentID}");
                    }
                }

                if (_isRootObjectSelector(value))
                {
                    InternalCollection.Add(value);
                }
            }
        }


        public ReadOnlyDictionary<TKey, TValue>     Map                { get; }
        public ReadOnlyObservableCollection<TValue> ReadonlyCollection { get; }
        public ObservableCollection<TValue>         Collection         => InternalCollection;
    }
}